import { Heart } from './heart'

interface Options{
	url: string;
	heartTime: number;
	heartMsg: string;
	isReconnect: boolean;
	isDestroy: boolean;
	reconnectTime: number;
	reconnectCount: number;
	openCb?: (event:any)=>void;
	closeCb?: (event:any)=>void;
	messageCb?: (event:any)=>void;
	errorCb?: (event:any)=>void;
	isHeart: boolean;
}

//通过 extends 实现类的继承。
export class Socket extends Heart {
	//实例属性：定义在实例对象（ this ）上的属性。
	ws:WebSocket = null

	RECONNECT_TIMER:number = null // 重连计时器
	RECONNECT_COUNT:number = 10 // 变量保存，防止丢失

	OPTIONS:Options = {
		url: null, // 链接的通道的地址
		heartTime: 5000, // 心跳时间间隔
		heartMsg: 'ping', // 心跳信息,默认为'ping'
		isReconnect: true, // 是否自动重连,默认自动重连，设置false则不自动重连
		isDestroy: false, // 是否销毁，执行销毁方法后变为true
		reconnectTime: 5000, // 重连时间间隔
		reconnectCount: 5, // 重连次数 -1 则不限制
		openCb: null, // 连接成功的回调
		closeCb: null, // 关闭的回调
		messageCb: null, // 消息的回调
		errorCb: null, // 错误的回调
		isHeart:true //是否开启心跳,默认开启
	}

	//constructor 方法是类的默认方法，创建类的实例化对象时被调用，即使用new关键字的时候。
	/**
	 *
	 * @param {Options} ops
	 */
	constructor(ops:Options) {
		//子类 constructor 方法中必须有 super ，且必须出现在 this 之前。
		super(ops.heartTime)
		//将实例化时传入的参数与默认值合并
		Object.assign(this.OPTIONS, ops)
		this.create()
	}
	/**
	 * 建立连接
	 */
	create() {
		//判断浏览器是否支持websocket
		if (!('WebSocket' in window)) {
			/* eslint-disable no-new */
			new Error('当前浏览器不支持，无法使用')
			return
		}
		//判断websocket的地址是否设置
		if (!this.OPTIONS.url) {
			new Error('地址不存在，无法建立通道')
			return
		}
		//删除以前的websocket实例
		delete this.ws
		//创建新的websocket实例
		this.ws = new WebSocket(this.OPTIONS.url)
		//自定义连接成功事件，用于指定连接成功后的回调函数。
		this.onopen(null)
		//自定义关闭事件，用于指定连接关闭后的回调函数。
		this.onclose(null)
		//自定义消息监听事件，用于指定收到服务器数据后的回调函数。
		this.onmessage(null)
		//自定义错误监听事件，用于指定发生错误后的回调函数。
		this.onerror(null)
	}
	/**
	 * 自定义连接成功事件
	 * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
	 * @param {Function} callback 回调函数
	 */
	onopen(callback:(event:Event)=>void) {
		this.ws.onopen = (event) => {
			clearTimeout(this.RECONNECT_TIMER) // 清除重连定时器
			this.OPTIONS.reconnectCount = this.RECONNECT_COUNT // 计数器重置
			// 如果开启了心跳功能，建立心跳机制
			this.OPTIONS.isHeart&&super.reset().start(() => {
				//每隔一段时间向服务器发送心跳消息，时间间隔通过基类Heart的timeout指定
				this.send(this.OPTIONS.heartMsg)
			})
			//回调函数的执行,传递了回调函数才会执行
			//如果传递了回调函数，就用传过来的回调函数，否则如果在实例化的时候传递了给openCb传递了函数的话就用该传递的函数
			if (typeof callback === 'function') {
				callback(event)
			} else {
				typeof this.OPTIONS.openCb === 'function' && this.OPTIONS.openCb(event)
			}
		}
	}
	/**
	 * 自定义关闭事件
	 * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
	 * @param {Function} callback 回调函数
	 */
	onclose(callback:(event:CloseEvent)=>void) {
		this.ws.onclose = (event) => {
			//清除心跳
			super.reset()
			//没有执行销毁动作而出现websocket关闭情况下执行重连
			!this.OPTIONS.isDestroy && this.OPTIONS.isReconnect && this.onReconnect()
			//关闭后的回调函数
			if (typeof callback === 'function') {
				callback(event)
			} else {
				typeof this.OPTIONS.closeCb === 'function' && this.OPTIONS.closeCb(event)
			}
		}
	}
	/**
	 * 自定义错误事件
	 * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
	 * @param {Function} callback 回调函数
	 */
	onerror(callback:(event:Event)=>void) {
		this.ws.onerror = (event) => {
			//出错的回调函数
			if (typeof callback === 'function') {
				callback(event)
			} else {
				typeof this.OPTIONS.errorCb === 'function' && this.OPTIONS.errorCb(event)
			}
		}
	}
	/**
	 * 自定义消息监听事件
	 * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
	 * @param {Function} callback 回调函数
	 */
	onmessage(callback:(event:MessageEvent)=>void) {
		this.ws.onmessage = (event) => {
			// 收到任何消息，重新开始倒计时心跳检测
			this.OPTIONS.isHeart&&super.reset().start(() => {
				this.send(this.OPTIONS.heartMsg)
			})
			//接收到消息的回调函数
			if (typeof callback === 'function') {
				callback(event.data)
			} else {
				typeof this.OPTIONS.messageCb === 'function' && this.OPTIONS.messageCb(event.data)
			}
		}
	}
	/**
	 * 自定义发送消息事件
	 * @param {String} data 发送的文本
	 */
	send(data:string) {
		if (this.ws.readyState !== this.ws.OPEN) {
			new Error('没有连接到服务器，无法推送')
			return
		}
		this.ws.send(data)
	}
	/**
	 * 连接事件
	 */
	onReconnect() {
		//如果重连次数大于0或者等于-1，每隔reconnectTime时间重新尝试建立websocket连接
		if (this.OPTIONS.reconnectCount > 0 || this.OPTIONS.reconnectCount === -1) {
			//重连计时器
			this.RECONNECT_TIMER = setTimeout(() => {
				//建立连接
				this.create()
				//如果有设置重连次数且不等于-1，则重连次数减1
				if (this.OPTIONS.reconnectCount !== -1) this.OPTIONS.reconnectCount--
			}, this.OPTIONS.reconnectTime)
		} else {
			//重连次数等于0，清除重连计时器
			clearTimeout(this.RECONNECT_TIMER)
			//初始化重连次数
			this.OPTIONS.reconnectCount = this.RECONNECT_COUNT
		}
	}
	/**
	 * 销毁
	 */
	destroy() {
		super.reset()//清除心跳
		clearTimeout(this.RECONNECT_TIMER) // 清除重连定时器
		this.OPTIONS.isDestroy = true //是否销毁赋值为true
		this.ws.close()//关闭websocket
	}
}
